package org.aldar.bquest.logic.controller.level;

import android.util.Log;
import org.aldar.bquest.constants.IConstants;
import org.aldar.bquest.constants.LandscapeType;
import org.aldar.bquest.entity.LandscapeArea;
import org.aldar.bquest.entity.LandscapeItem;
import org.aldar.bquest.entity.LandscapeSprite;
import org.aldar.bquest.entity.LsTextureDef;
import org.aldar.bquest.entity.pool.TilesPool;
import org.aldar.bquest.logic.controller.entity.HeroAnimationController;
import org.aldar.bquest.profile.GameProfile;
import org.aldar.bquest.resources.ResourceManager;
import org.aldar.bquest.screens.MainBoardScreen;
import org.aldar.bquest.util.Logger;
import org.aldar.bquest.util.TestUtil;
import org.andengine.entity.Entity;
import org.andengine.entity.modifier.MoveXModifier;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.util.math.MathUtils;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: Daria
 * Date: 4/22/13
 * Time: 10:07 PM
 * To change this template use File | Settings | File Templates.
 */
public class TimeLine implements IConstants {

    private static final Logger LOG = new Logger(TimeLine.class);

    private static float[] CASTLES_WIDTH = {160, 135, 125, 120, 160, 150, 150, 130, 279};
    private static float[] END_CASTLES_START_X = {25, 18, 25, 33, 0, 0, 0, 0, 30};
    private static float[] END_CASTLES_BASE_START_X = {0, 0, 0, 0, 0, 0, 0, 0, 0};

    //public static int getStartLsX() = 75;
    public static int LANDSCAPE_ITEM_WIDTH = 25;

    private Sprite startCastleBack = null;
    private Sprite endCastleBack = null;
    private Sprite startCastleFront = null;
    private Sprite endCastleFront = null;


    private int currentItemIndex = 0;
    private int currentMovingStateIndex = 0;

    private static int CRYSTAL_CASTLE_WIDTH = 100;

    private boolean currentLandscapeReached = true;

    private List<LandscapeItem> levelItems = null;
    private List<AnimatedSprite> arrows = null;
    private Map<Integer, AnimatedSprite> enemyMap = new HashMap<Integer, AnimatedSprite>();

    private boolean levelFinished;
    private ITextureRegion startCastleFrontRegion = null;
    private ITextureRegion endCastleFrontRegion = null;
    private float endCastleWidth;
    private float startCastleWidth;

    public boolean isLevelFinished() {
        return levelFinished;
    }

    public void initLevel(LevelDef currentLevel) {
        currentItemIndex = 0;
        currentMovingStateIndex = 0;
        levelFinished = false;
        initStartCastle();
        Log.i("loony", "generating level items!");
        if (currentLevel.getLandscapeAreas() != null) {
            levelItems = generateLevelItemsFromAreas(currentLevel.getLandscapeAreas(), currentLevel.getEnemyIndexes());
        } else {
            levelItems = generateRandomLevel(currentLevel.getLength(), currentLevel.getMinLandscapeLength(), currentLevel.getMaxLandscapeLength());
        }
        initHero();
        initFinishCastle();
        HeroAnimationController.getInstance().moveHeroToLevelStart();
    }

    private List<LandscapeItem> generateLevelItemsFromAreas(LandscapeArea[] landscapeAreas, List<Integer> enemyIndexes) {
        List<LandscapeItem> result = new LinkedList<LandscapeItem>();
        arrows = new LinkedList<AnimatedSprite>();
        LandscapeType prevStyle = null;
        int curIndex = 0;
        int totalConcerts = 0;
        for (int i = 0; i < landscapeAreas.length; i++) {
            LOG.i("Generating level items starting from next i=" + i);
            int lsSequenceLength = landscapeAreas[i].getAreaLength();
            int prevLsSteps = -1;
            LandscapeType style = landscapeAreas[i].getType();
            int townNotes = 0;
            int totalNotes = 0;
            int noteIndex = 0;
            float lsMiddleX = 0;
            LsTextureDef prevTextureDef = null;
            Entity townJewels = null;
            int lsIndex = 0;
            for (int j = 0; j < lsSequenceLength; ) {
                if (j == 0 && landscapeAreas[i].getType().allowsConcerts() == true) {
                    townNotes = landscapeAreas[i].getNotesForTown();
                    //FOXME this is a hack! must fix all levels with notes>3 manually!
                    if (townNotes > 3) townNotes = 3;
                    totalNotes = townNotes;
                    totalConcerts += townNotes;
                    lsMiddleX = getStartLsX() + LANDSCAPE_ITEM_WIDTH * result.size() + LANDSCAPE_ITEM_WIDTH * lsSequenceLength / 2f;

                    GameProfile.getCurrentLevelProfile().getRemainingConcertsByTown().put(GameProfile.getCurrentLevelProfile().getRemainingConcertsByTown().size(), townNotes);
                }

                int remainSteps = lsSequenceLength - j;
                LsTextureDef textureDef = null;
                if (landscapeAreas[i].getLsIndices() != null && landscapeAreas[i].getLsIndices().length > 0) {
                    textureDef = ResourceManager.getInstance().getLandScapeForStyleByIndex(style, landscapeAreas[i].getLsIndices()[lsIndex++]);
                } else {
                    textureDef = ResourceManager.getInstance().getMaxLandScapeForStyle(style, remainSteps);
                    int attempt = 0;
                    while (textureDef == prevTextureDef && attempt < 7) {
                        synchronized (TimeLine.class) {
                            attempt++;
                        }
                        textureDef = ResourceManager.getInstance().getMaxLandScapeForStyle(style, remainSteps);
                    }
                }
                prevTextureDef = textureDef;
                for (int k = 0; k < textureDef.getSteps(); k++) {
                    LandscapeItem item = new LandscapeItem();
                    item.setLandscapeType(style);
                    result.add(item);

                    if (k == 0) {
                        float placeX = getStartLsX() + LANDSCAPE_ITEM_WIDTH * curIndex + (textureDef.getSteps() * LANDSCAPE_ITEM_WIDTH / 2);
                        LandscapeSprite sprite = new LandscapeSprite(placeX, CAMERA_HEIGHT - 200f,
                                textureDef.getTextureRegion() != null ? textureDef.getTextureRegion() : textureDef.getBaseTextureRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                        sprite.setAnchorCenter(0.5f, 0);
                        item.setLandscapeSprite(sprite);
                        MainBoardScreen.getScm().getTimeLayer().attachChild(sprite);
                    }

                    Sprite lsBaseSprite = new Sprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * (result.size() - 1) + LANDSCAPE_ITEM_WIDTH / 2, GROUND_Y - 7f,
                            textureDef.getBaseTextureRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                    lsBaseSprite.setAnchorCenter(0.5f, 0);
                    item.setLsBasementSprite(lsBaseSprite);
                    MainBoardScreen.getScm().getLandscapeBaseLayer().attachChild(lsBaseSprite);

                    //add town note!
                    if (townNotes > 0) {
                        Entity noteContainer = new Entity(lsMiddleX, CAMERA_HEIGHT - 70);
                        Entity jewel = TilesPool.getEntityForTexture(ResourceManager.getInstance().getTownJewels()[totalNotes - 1]);
                        noteContainer.attachChild(jewel);
                        MainBoardScreen.getScm().getBgLayer().attachChild(noteContainer);
                        item.setTownNote(noteContainer);
                        //add information about the concerts count into the level profile
                        GameProfile.getCurrentLevelProfile().getConcertsByTown().add(noteContainer);
                        townNotes = 0;
                    }

                    if (enemyIndexes.contains(result.size() - 1)) {
                        final AnimatedSprite enemyImp = new AnimatedSprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * (result.size() - 1), GROUND_Y, ResourceManager.getInstance().getEnemyImpRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                        enemyImp.setAnchorCenter(0.15f, 0);
                        enemyImp.animate(50);
                        MainBoardScreen.getScm().getBonusLayer().attachChild(enemyImp);
                        enemyMap.put(result.size() - 1/*index for imp*/, enemyImp);
                    }

                    //add arrow for the future
                    final AnimatedSprite arrow = new AnimatedSprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * (result.size() - 1), GROUND_Y, ResourceManager.getInstance().getHasteArrowRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                    arrow.setAnchorCenter(0.5f, 0);
                    arrow.setAlpha(0f);
                    //arrow.setVisible(false);
                    MainBoardScreen.getScm().getLandscapeBaseLayer().attachChild(arrow);
                    arrows.add(arrow);

                }
                j += textureDef.getSteps();
                curIndex += textureDef.getSteps();
                prevLsSteps = textureDef.getSteps();
            }
            LOG.i("Generating level items lsFinished, next i=" + i + ", levelLength=" + landscapeAreas.length);
            prevStyle = style;
            GameProfile.getCurrentLevelProfile().setTotalConcerts(totalConcerts);
        }
        LOG.i("Generating level items FINISHED, result size =" + result.size());
        return result;
    }

    private static void initHero() {
        HeroAnimationController.getInstance().initHero();
    }

    private void initStartCastle() {
        //attach start castle
        ITextureRegion startCastleBackRegion = null;
        float anchorX = 0;

        //if (GameProfile.selectedLevelIndex == 0) {
        startCastleBackRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex)[0];
        startCastleFrontRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex)[1];
        startCastleWidth = CASTLES_WIDTH[GameProfile.selectedLevelAreaIndex];
        //}
        //crystals at level start and end removed now!
        /*else {
            startCastleBackRegion = ResourceManager.getInstance().getCastleCrystalBackRegion();
            startCastleFrontRegion = ResourceManager.getInstance().getCastleCrystalFrontRegion();
            startCastleWidth = CRYSTAL_CASTLE_WIDTH;
            anchorX = 33f / 160f;
        } */
        if (startCastleBack == null) {
            startCastleBack = new LandscapeSprite(0, GROUND_Y - 7f, startCastleBackRegion
                    , MainBoardScreen.getScm().getVertexBufferObjectManager());
            MainBoardScreen.getScm().getTimeLayer().attachChild(startCastleBack);
        }
        startCastleBack.setPosition(0, GROUND_Y - 7f);
        startCastleBack.setAnchorCenter(anchorX, 0);

        if (startCastleFront == null) {
            startCastleFront = new LandscapeSprite(0, GROUND_Y - 7f, startCastleFrontRegion, MainBoardScreen.getScm().getVertexBufferObjectManager());
            startCastleFront.setAnchorCenter(0, 0);
            MainBoardScreen.getScm().getCastleFrontLayer().attachChild(startCastleFront);
        }
        startCastleFront.setAnchorCenter(anchorX, 0);
        startCastleFront.setPosition(0, GROUND_Y - 7f);
    }

    private void initFinishCastle() {
        //attach end castle
        ITextureRegion endCastleBackRegion = null;
        float anchorX = 0;
        if (GameProfile.selectedLevelIndex == LevelDefinition.getLevels()[GameProfile.selectedLevelAreaIndex].getAreaLevels().size() - 1) {
            endCastleBackRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex)[2];
            endCastleFrontRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex)[3];
            endCastleWidth = CASTLES_WIDTH[GameProfile.selectedLevelAreaIndex + 1];
            anchorX = END_CASTLES_START_X[GameProfile.selectedLevelAreaIndex + 1] / endCastleWidth;
        } else {
            //crystals at level finish replaced with castle
            endCastleBackRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex - 1)[2];
            endCastleFrontRegion = ResourceManager.getInstance().getCastlesBySetMap().get(GameProfile.selectedLevelAreaIndex - 1)[3];
            endCastleWidth = CASTLES_WIDTH[GameProfile.selectedLevelAreaIndex];
            anchorX = END_CASTLES_START_X[GameProfile.selectedLevelAreaIndex] / endCastleWidth;
        }

        if (endCastleBack == null) {
            endCastleBack = new LandscapeSprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * levelItems.size(), GROUND_Y - 7f,
                    endCastleBackRegion, MainBoardScreen.getScm().getVertexBufferObjectManager());
            endCastleBack.setAnchorCenter(anchorX, 0);
            MainBoardScreen.getScm().getTimeLayer().attachChild(endCastleBack);
        }
        endCastleBack.setPosition(getStartLsX() + LANDSCAPE_ITEM_WIDTH * levelItems.size(), GROUND_Y - 7f);
        if (endCastleFront == null && endCastleFrontRegion != null) {
            endCastleFront = new LandscapeSprite(
                    getStartLsX() + LANDSCAPE_ITEM_WIDTH * levelItems.size(), GROUND_Y - 7f,
                    endCastleFrontRegion, MainBoardScreen.getScm().getVertexBufferObjectManager());
            endCastleFront.setAnchorCenter(anchorX, 0);
            MainBoardScreen.getScm().getCastleFrontLayer().attachChild(endCastleFront);
            endCastleFront.setPosition(getStartLsX() + LANDSCAPE_ITEM_WIDTH * levelItems.size(), GROUND_Y - 7f);
        }
    }

    private List<LandscapeItem> generateRandomLevel(int levelLength, int minLsLength, int maxLsLength) {
        List<LandscapeItem> result = new LinkedList<LandscapeItem>();
        arrows = new LinkedList<AnimatedSprite>();
        LandscapeType prevStyle = null;
        for (int i = 0; i < levelLength; ) {
            LOG.i("Generating level items starting from next i=" + i);
            LandscapeType style = LandscapeType.values()[MathUtils.random(0, 4)];
            while (style == prevStyle) {
                style = LandscapeType.values()[MathUtils.random(0, 4)];
            }
            int lsSequenceLength = MathUtils.random(minLsLength, maxLsLength);
            int prevLsSteps = -1;
            for (int j = 0; j < lsSequenceLength; ) {
                int remainSteps = lsSequenceLength - j;
                LsTextureDef textureDef = ResourceManager.getInstance().getMaxLandScapeForStyle(style, remainSteps);
                int attempt = 0;
                while (textureDef.getSteps() > remainSteps ||
                        (textureDef.getSteps() == prevLsSteps && remainSteps > 1 && attempt < 5)) {
                    synchronized (TimeLine.class) {
                        attempt++;
                    }
                    textureDef = ResourceManager.getInstance().getMaxLandScapeForStyle(style, remainSteps);
                }
                for (int k = 0; k < textureDef.getSteps(); k++) {
                    LandscapeItem item = new LandscapeItem();
                    item.setLandscapeType(style);
                    result.add(item);
                    if (k == 0) {
                        float placeX = getStartLsX() + LANDSCAPE_ITEM_WIDTH * i + (textureDef.getSteps() * LANDSCAPE_ITEM_WIDTH / 2);
                        LandscapeSprite sprite = new LandscapeSprite(placeX, CAMERA_HEIGHT - 200f,
                                textureDef.getTextureRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                        sprite.setAnchorCenter(0.5f, 0);
                        item.setLandscapeSprite(sprite);
                        MainBoardScreen.getScm().getTimeLayer().attachChild(sprite);
                    }

                    //add arrow for the future
                    final AnimatedSprite arrow = new AnimatedSprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * (result.size() - 1), GROUND_Y, ResourceManager.getInstance().getHasteArrowRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                    arrow.setAnchorCenter(0.5f, 0);
                    arrow.setAlpha(0f);
                    //arrow.setVisible(false);
                    MainBoardScreen.getScm().getHeroLayer().attachChild(arrow);
                    arrows.add(arrow);

                    if (result.size() % 6 == 3) {
                        final AnimatedSprite enemyImp = new AnimatedSprite(getStartLsX() + LANDSCAPE_ITEM_WIDTH * (result.size() - 1), GROUND_Y, ResourceManager.getInstance().getEnemyImpRegion(), MainBoardScreen.getScm().getVertexBufferObjectManager());
                        enemyImp.setAnchorCenter(0.15f, 0);
                        enemyImp.animate(50);
                        MainBoardScreen.getScm().getHeroLayer().attachChild(enemyImp);
                        enemyMap.put(result.size() - 1/*index for imp*/, enemyImp);
                    }
                }
                j += textureDef.getSteps();
                i += textureDef.getSteps();
                prevLsSteps = textureDef.getSteps();
            }
            LOG.i("Generating level items lsFinished, next i=" + i + ", levelLength=" + levelLength);
            prevStyle = style;
        }
        LOG.i("Generating level items FINISHED, result size =" + result.size());
        return result;
    }

    public boolean proceedLevelSteps(int moves) {
        if (currentItemIndex >= levelItems.size()) {
            LOG.i("Level already finished - cannot move forward!");
            return false;
        }
        LOG.i("TimeLine: proceeding " + moves + " steps forward.");
        boolean proceedLevel = true;
        int adjustedMoves = moves;
        currentItemIndex += moves;
        if (currentItemIndex >= levelItems.size()) {
            LOG.i("Reached level finish!");
            adjustedMoves = currentItemIndex - levelItems.size() + 1; //move only the remaining landscapes
            currentItemIndex = levelItems.size();
            proceedLevel = false;
            LevelController.getInstance().setCurrentLevelState(LevelController.LevelState.FINISHED);
            levelFinished = true;
        }
        HeroAnimationController.getInstance().animateWalkStep(adjustedMoves);
        return proceedLevel;
    }

    public static void moveLeft(Entity sprite) {
        if (sprite != null) {
            sprite.registerEntityModifier(new MoveXModifier(HeroAnimationController.HERO_STEP_DURATION, sprite.getX(), sprite.getX() - LANDSCAPE_ITEM_WIDTH));
        }
    }

    public List<LandscapeItem> getLevelItems() {
        return levelItems;
    }

    public int getCurrentItemIndex() {
        return currentItemIndex;
    }

    public void setCurrentItemIndex(int currentItemIndex) {
        this.currentItemIndex = currentItemIndex;
    }

    public LandscapeItem getCurrentLandscape() {
        LOG.i("Current item index: " + currentItemIndex);
        if (currentItemIndex < levelItems.size()) {
            return getLevelItems().get(currentItemIndex);
        } else {
            return null;
        }
    }

    public int getCurrentMovingStateIndex() {
        return currentMovingStateIndex;
    }

    public Sprite getStartCastleBack() {
        return startCastleBack;
    }

    public Sprite getEndCastleBack() {
        return endCastleBack;
    }

    public Sprite getStartCastleFront() {
        return startCastleFront;
    }

    public Sprite getEndCastleFront() {
        return endCastleFront;
    }

    public int increaseCurrentMovingStateIndex() {
        currentMovingStateIndex++;
        return currentMovingStateIndex;
    }

    public boolean isCurrentLandscapeReached() {
        return currentLandscapeReached;
    }

    public void setCurrentLandscapeReached(boolean currentLandscapeReached) {
        this.currentLandscapeReached = currentLandscapeReached;
        TestUtil.getInstance().updateText("reachedCurrent", "" + currentLandscapeReached);
    }

    public List<AnimatedSprite> getArrows() {
        return arrows;
    }

    public Map<Integer, AnimatedSprite> getEnemyMap() {
        return enemyMap;
    }

    public int getTargetEnemyIndex() {
        int movingIndex = LevelController.getTimeLine().getCurrentMovingStateIndex();
        int enemyIndex = 0;
        for (int i : enemyMap.keySet()) {
            //LOG.i("searching for next enemy index in front of hero, next is..." + i);
            if (i <= movingIndex) {
                //LOG.i("...is already behind the hero moving index which is..." + movingIndex);
                continue;
            }
            if (i < enemyIndex || enemyIndex == 0) { //if not yet set or found smaller index - set it!
                enemyIndex = i;
            }
        }
        if (enemyIndex == 0) {
            return 0; //no enemy sprite left on timeline in front of hero
        } else {
            //LOG.i("hooray, next enemy Index is " + enemyIndex);
        }
        return enemyIndex;
    }

    public float getStartLsX() {
        return startCastleWidth;
    }

    public float getEndCastleWidth() {
        return endCastleWidth;
    }
}


